package core

import (
	"encoding/json"
	"freechat-im/external"
	"freechat-im/model"
	"github.com/gorilla/websocket"
	"github.com/sirupsen/logrus"
	"net/http"
	"strconv"
	"sync"
	"time"
)

var serviceInstance *ChatService
var messageHandle *MessageHandler

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

type ChContext struct {
	clients    map[*Client]bool
	broadcast  chan []byte
	register   chan *Client
	unregister chan *Client
	users      map[string]*Client // map[key=userId,value=client连接]
	sync.Mutex
}

// LocalOnline 用户是否连接到了此节点
func (c *ChContext) LocalOnline(userId string) bool {
	_, exists := c.users[userId]
	return exists
}

type ChatService struct {
	//管理users和connection的对象
	context        *ChContext
	onlineRegister OnlineUserRegister
}

func GetService() *ChatService {
	return serviceInstance
}

// Start 入口方法 mode: 0-单机启动、1-cluster 集群启动
func Start(mode int) *ChatService {
	service := &ChatService{
		context: &ChContext{
			broadcast:  make(chan []byte),
			register:   make(chan *Client),
			unregister: make(chan *Client),
			clients:    make(map[*Client]bool),
			users:      make(map[string]*Client),
		},
	}
	service.onlineRegister = func() OnlineUserRegister {
		if mode == 0 {
			return NewStandaloneOnlineUserRegister(service.context)
		} else {
			return NewClusterOnlineUserRegister(service.context)
		}
	}()

	go service.run()

	messageHandle = NewMessageHandle(service.onlineRegister)
	serviceInstance = service
	return serviceInstance
}

func (service *ChatService) run() {
	var h = service.context
	for {
		select {
		//有新user进来时
		case client := <-h.register:
			h.Lock()
			h.clients[client] = true
			h.Unlock()
		//有user断开时
		case client := <-h.unregister:
			h.Lock()
			if _, ok := h.clients[client]; ok {
				delete(h.clients, client)
				close(client.send)
			}
			h.Unlock()
		//有消息广播时
		case message := <-h.broadcast:
			h.Lock()
			//遍历全部client，广播消息
			for client := range h.clients {
				select {
				case client.send <- &Data{Bytes: message}:
				default:
					close(client.send)
					delete(h.clients, client)
				}
			}
			h.Unlock()
		}
	}
}

// HandleWebSocket 启动websocket
func (service *ChatService) HandleWebSocket(w http.ResponseWriter, r *http.Request) {
	userId, err := external.AuthHandle(r)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	//升级http连接为websocket
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		logrus.Error("升级http连接为websocket时错误. err = ", err.Error())
		http.Error(w, err.Error(), http.StatusInternalServerError)
		conn.Close()
		return
	}

	//用户连接成功
	//创建client，持有connection
	client := &Client{conn: conn, send: make(chan *Data, 256)}
	client.conn.SetCloseHandler(func(code int, text string) error {
		logrus.Debug("用户已断开连接... userId=", userId)

		delete(service.context.users, userId)
		service.context.unregister <- client
		service.onlineRegister.CheckOut(userId)
		return nil
	})
	logrus.Debug("用户连接成功... userId=", userId)

	service.context.register <- client
	service.context.users[userId] = client
	//连接成功后，记录用户连接状态到Redis
	service.onlineRegister.CheckIn(userId)

	//拉取离线表中的message
	go func() {
		time.Sleep(3 * time.Second) // 延迟3秒钟
		// 起始时间为一个月前的时间
		var timestamp = time.Now().AddDate(0, -1, 0).UnixMilli()
		for {
			messages, tailTime := external.GetOfflineMessages(userId, timestamp)

			for _, message := range messages {
				service.SendToClient(message, message.Receiver)
			}
			if len(messages) < 1000 { //因为有限制一次只能查1000条，<1000就说明已经查到末尾了
				break
			}
			timestamp = tailTime + 1
		}
	}()

	//写消息：往client的connection中写消息，加一个发送后的回调
	go client.writePump(messageHandle)
	//读消息：从connection中读取消息
	client.readPump(service.context, userId, messageHandle)
}

// SendToClient 发送消息到Client端 msg: 应该不能传指针
func (service *ChatService) SendToClient(msg any, sendTo string) {
	if targetClient, ok := service.context.users[sendTo]; ok {
		var bytes []byte = nil
		var meta *map[string]string = nil

		if str, ok := msg.(string); ok {
			bytes = []byte(str)
		} else {
			var messageWrap = &model.MessageWrap{}

			if cm, ok := msg.(model.ChatMessage); ok {
				messageWrap.Message = &cm
				meta = &map[string]string{"msgId": strconv.FormatInt(cm.Id, 10), "receiver": cm.Receiver}
			}
			if cme, ok := msg.(model.MsgEvent); ok {
				messageWrap.Event = &cme
			}
			bytes, _ = json.Marshal(messageWrap) //序列化为json
		}
		targetClient.send <- &Data{Bytes: bytes, Meta: meta}
	} else {
		logrus.Info("发送消息时，user未连接: " + sendTo)
	}
}
